"""
Token工具包
"""

from datetime import datetime, timedelta
from typing import Optional, Union

import jwt
from jwt import ExpiredSignatureError, InvalidTokenError

from application.config.ServerConfig import ServerConfig
from application.util.RedisUtil import RedisUtil
from application.util.StringUtil import base64_encode, random_uuid
from application.exception.BasicException import BasicException
from application.enumeration.StatusCodeEnum import StatusCodeEnum

# redis客户端
redis_client: RedisUtil = RedisUtil()


def generate_token(user_id: int) -> str:
    """
    根据用户ID生成 JWT Token，并缓存到 Redis
    :param user_id: 用户ID
    :return: token 字符串
    """
    # 当前时间
    now = datetime.utcnow()
    expire = now + timedelta(seconds=ServerConfig.token_expire)

    redis_key = f"user_token:{user_id}"
    token = redis_client.get_value(key=redis_key)  # 删除redis缓存的token

    try:
        payload = jwt.decode(token, key=ServerConfig.secret_key, algorithms=["HS256"])
        if payload.get("exp") and payload.get("exp") > now.timestamp():
            return token
    except Exception:
        # # 删除旧Token（如需）
        delete_exist_token(user_id=user_id)

    # 生成额外信息
    other_str = base64_encode(f"{random_uuid()}{user_id}{int(now.timestamp())}")

    # 构建JWT载荷
    payload = {
        "user_id": user_id,
        "baby": other_str,
        "iat": now,
        "exp": expire,
    }

    # 编码成JWT
    token = jwt.encode(payload=payload, key=ServerConfig.secret_key, algorithm="HS256")

    # # 删除旧Token（如需）
    # delete_exist_token(user_id=user_id)

    # 缓存新的Token
    redis_client.set_value(key=redis_key, value=token, ex=ServerConfig.token_expire)

    return token


def verify_token(token: str) -> Union[int, None]:
    """
    验证 JWT Token 是否有效
    :param token: JWT token
    :return: user_id（有效）或 None（无效）
    """
    try:
        # 解码 Token，自动验证 exp 字段
        payload: dict = jwt.decode(jwt=token, key=ServerConfig.secret_key, algorithms=["HS256"])
        user_id = payload.get("user_id")
        if not user_id:
            print("not user_id")
            raise BasicException(status_code=StatusCodeEnum.AUTHORITY_ERROR.value,
                                 error_message="非法访问，请先登录")

        # Redis key
        redis_key = f"user_token:{user_id}"
        cached_token = redis_client.get_value(key=redis_key)

        # 比较 token
        if cached_token != token:
            print("token 不一致")
            raise BasicException(status_code=StatusCodeEnum.AUTHORITY_ERROR.value,
                                 error_message="非法访问，请先登录")

        return user_id  # 验证通过，返回 user_id

    except ExpiredSignatureError:
        # Token 已过期
        print("token 已过期")
        raise BasicException(status_code=StatusCodeEnum.AUTHORITY_ERROR.value,
                             error_message="非法访问，请先登录")
    except InvalidTokenError:
        # 无效 Token（篡改、签名错误等）
        print("无效 Token（篡改、签名错误等）")
        raise BasicException(status_code=StatusCodeEnum.AUTHORITY_ERROR.value,
                             error_message="非法访问，请先登录")
    except Exception as e:
        # 其他异常
        print("其他异常")
        raise BasicException(status_code=StatusCodeEnum.AUTHORITY_ERROR.value,
                             error_message="非法访问，请先登录")


def clear_token(user_id: int) -> bool:
    """
    清除token
    :param user_id: user_id
    :return: None
    """
    try:
        key = f"user_token:{user_id}"
        # 从redis中获取token
        redis_token: Optional[str] = redis_client.get_value(key)
        if redis_token is None:
            return False
        redis_client.delete_by_key(key)  # 删除redis缓存的token
        return True
    except Exception:
        return False


def delete_exist_token(user_id: int) -> None:
    """
    删除同一个用户的token，保证用户只能有一个token
    :param user_id: 用户ID
    :return: None
    """
    redis_client.delete_by_key(key=str(user_id))  # 删除token


def get_user_id(token: str) -> int:
    """
    获取用户id
    :param token: token
    :return: 用户id | 0
    """
    try:
        payload: dict = jwt.decode(jwt=token, key=ServerConfig.secret_key, algorithms=["HS256"])
        user_id: int = int(payload.get("user_id"))
        # 从redis中获取token
        redis_token: Optional[str] = redis_client.get_value(key=str(user_id))
        if redis_token != token:
            return 0
        return user_id
    except Exception:
        return 0
